﻿using Assets.Scripts.DL.Enemy.BaseClass.Controller;
using Assets.Scripts.DL.Enemy.BaseClass.FSM;
using Assets.Scripts.DL.Enemy.CommonEnemy.States;
using Assets.Scripts.DL.Enemy.CommonEnemy.States.ATK;
using Assets.Scripts.DL.Enemy.CommonEnemy.States.CommonState;
using Assets.Scripts.DL.Enemy.CommonEnemy.States.Special;
using Assets.Scripts.DL.Mgrs;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Assets.Scripts.DL.Enemy.CommonEnemy {
	public class Controller : EnemyControllerBase<StateEnum> {

		[Header("Debug")]
		//public float afteratkTime;

		protected HFSM<Controller, StateEnum> _rootHFSM;

		public Config Config;
		public StateData StateData;
		BaseAtkDetect _atkDetect;


		protected override void Awake() {
			base.Awake();

			_animeMgr = new AnimeMgr<StateEnum>(_animator);
			_rigidBodyMgr = new RigidBodyMgr(_rigidbody, Config.InitGravity);
			_rootHFSM = new RootHFSM(this, StateEnum.RootHFSM);
			StateData = new StateData {
				CurHP = Config.MaxHP
			};
			InitAtkTimer();
			InitAnimMgr();
			InitStates();
			_rootHFSM.Enter();
			_atkDetect = new BaseAtkDetect(transform, GetLayer(LayerStrings.Player));

		}

		protected override void Update() {
			_rootHFSM.Update();
#if UNITY_EDITOR
			Debug();

#endif
		}

		public virtual Vector3 GetPlayerPos() {
			throw new NotImplementedException();
		}

		public virtual Vector2 GetPlayerVelocity() {
			throw new NotImplementedException();
		}

		public bool CheckAtk(Vector2 offset, float range, int damage)
			=> _atkDetect.CheckAtk(offset, range, damage);

		void Debug() {
			var _transform = transform;
			var t = new Vector2(_transform.position.x, _transform.position.y);
			var faceDir = new Vector2(_transform.localScale.x, 0);
			//var wallOff = new Vector2(FaceDir * Config.WallOff.x, 0);
			//var atkOff = new Vector2(FaceDir * Config.AtkOff.x, 0);
			DebugTool.DrawRay(t + Config.GrdLeft, Vector2.down, Config.GrdDis, Color.green);
			DebugTool.DrawRay(t + Config.GrdRight, Vector2.down, Config.GrdDis, Color.green);
			DebugTool.DrawRay(GetRealXDirPos(Config.WallOff), faceDir, Config.WallDis, Color.green);
			DebugTool.DrawRay(GetRealXDirPos(Config.AtkOff), faceDir, Config.AtkDis, Color.green);
			DebugTool.DrawRay(GetRealXDirPos(Config.ViewOff), faceDir, Config.ViewDis, Color.green);
			//CanAtk();
		}
		//根据facedir确定判定的位置
		protected Vector2 GetRealXDirPos(Vector2 off) {
			var t = new Vector2(transform.position.x, transform.position.y);
			return t + new Vector2(off.x * FaceDir, off.y);
		}

		//#warning  后面要改
		public override bool CanAtk()
			=> CheckCd();

		protected virtual bool CheckCd() {
			if (StateData.LastAtkTime.Elapsed < Config.EachAtkGap) {
				//UnityEngine.Debug.Log("LastFail");
				return false;
			}
			foreach (var timer in StateData.AtkCdRecoder) {
				if (timer.Value.Elapsed > Config.CommonAtkGap)//这里冷却本想做成每个技能有对应的冷却的，但是scriptable好像不支持字典。
					return true;
			}
			//UnityEngine.Debug.Log("NocdOk");
			return false;
		}

		public virtual bool CheckSpecialCd() {
			throw new NotImplementedException();
		}

		public virtual bool DetectPlayerRemoteAtk() {
			throw new NotImplementedException();
		}

		public override bool IsPlayerInAtkRange()
			=> RayDetect(GetRealXDirPos(Config.AtkOff), Config.AtkDis, GetFaceDirVec(), GetLayer(LayerStrings.Player));

		public override bool IsFindPlayer()
			=> RayDetect(GetRealXDirPos(Config.ViewOff), Config.ViewDis, GetFaceDirVec(), GetLayer(LayerStrings.Player));

		public override bool IsTouchingWall()
			=> RayDetect(GetRealXDirPos(Config.WallOff), Config.WallDis, GetFaceDirVec(), GetLayer(LayerStrings.Wall) | GetLayer(LayerStrings.Ground));

		public override bool IsOnGround()
			=> IsTouchGround(Config.GrdLeft) && IsTouchGround(Config.GrdRight);

		bool IsTouchGround(Vector2 off) {
			var groundLayer = GetLayer(LayerStrings.Ground) | GetLayer(LayerStrings.Platform);
			return RayDetect(new Vector2(transform.position.x, transform.position.y) + off, Config.GrdDis, Vector2.down, groundLayer);
		}



		public override void TakeDamage(int damage) {
			//UnityEngine.Debug.Log("EHURT");
			StateData.CurHP -= damage;
			PoolTools.MakeDmgNumber(transform.position, damage);
			StateData.IsSpecialState = true;
			if (StateData.CurHP > 0)
				StateData.IsHurt = true;
			else
				StateData.IsDie = true;
		}

		protected override void InitAnimMgr() {
			AddAnim(StateEnum.Move, "Walk");
			AddAnim(StateEnum.Idle, "Idle");
			AddAnim(StateEnum.Atk1, "Atk1");
			AddAnim(StateEnum.Atk2, "Atk2");
			AddAnim(StateEnum.Atk3, "Atk3");
			AddAnim(StateEnum.Hurt, "Hurt");
			AddAnim(StateEnum.Die, "Die");

		}
		//不需要，敌人么写detect类
		protected override void InitDetects() {
			throw new NotImplementedException();
		}

		protected override void InitStates() {
			//一般状态
			var commonHfsm = new CommonStateHFSM(this, StateEnum.CommonHFSM);
			var move = new Move(this, StateEnum.Move);
			var idle = new Idle(this, StateEnum.Idle);
			commonHfsm.InitSubStates(new Dictionary<StateEnum, EnemyState<StateEnum>> {
				{StateEnum.Idle, idle},
				{StateEnum.Move, move},
				}
			);

			//Atk
			var atkHFSM = new AtkHFSM(this, StateEnum.AtkHFSM);
			var atk1 = new Atk(this, StateEnum.Atk1);
			var atk2 = new Atk(this, StateEnum.Atk2);
			var atk3 = new Atk(this, StateEnum.Atk3);
			atkHFSM.InitSubStates(new Dictionary<StateEnum, EnemyState<StateEnum>> {
				{ StateEnum.Atk1, atk1 },
				{ StateEnum.Atk2, atk2 },
				{ StateEnum.Atk3, atk3 },
			});


			//Special
			var specialHFSM = new EnemySpecialHFSM(this, StateEnum.SpecialHFSM);
			var hurt = new Hurt(this, StateEnum.Hurt);
			var die = new Die(this, StateEnum.Die);
			specialHFSM.InitSubStates(new Dictionary<StateEnum, EnemyState<StateEnum>> {
				{ StateEnum.Die, die },{StateEnum.Hurt, hurt },
			}
			);

			//Root
			_rootHFSM.InitSubStates(new Dictionary<StateEnum, EnemyState<StateEnum>> {
				{StateEnum.CommonHFSM,  commonHfsm},
				{StateEnum.AtkHFSM,  atkHFSM},
				{StateEnum.SpecialHFSM, specialHFSM },
				}
			);
		}

		protected override void InitAtkTimer() {
			StateData.AtkCdRecoder = new Dictionary<StateEnum, Timer> {
				{StateEnum.Atk1, new Timer() },
				{StateEnum.Atk2, new Timer() },
				{StateEnum.Atk3, new Timer() },
			};
		}

		private void OnDrawGizmos() {
			Vector3 p = transform.position;
			var dir = transform.localScale.x;
			Func<Vector2, Vector3> getpos = (offset) => {
				var realOffset = new Vector3(offset.x * dir, offset.y, 0);
				return p + realOffset;
			};
			Gizmos.DrawWireSphere(getpos(Config.AtkOff), Config.AtkDis);
		}
	}
}
